/**
 * 维护企业微信容器
 */
import { getLogger, Service } from '@gnlab/utils'
import { MemSubject } from '@gnlab/utils/rx'
import type { Services } from '@/services'
import { API } from '@/services/common/api'

const logger = getLogger('services/container')

const CONTAINER_PERIOD = 10 * 60 * 1000 // 容器有效期（毫秒）

export enum Status {
  Empty = 1, // 尚未加载容器信息
  Loading = 2, // 正在加载容器信息
  Normal = 3, // 容器信息已加载
  Error = 4, // 容器信息加载失败
}

interface RespContainerData {
  createtime: string
  hostname: string
  qrcode: string
  version: string
}
export interface ContainerData {
  hostname: string
  qrcode: string
  created: Date
}

export type Usable =
  | { status: Status.Empty }
  | { status: Status.Loading }
  | { status: Status.Normal; data: ContainerData }
  | { status: Status.Error; message: string }

// 生成容器时需要记录“归属”信息，以使此容器暂时不分配给其他使用者
// 对于前端，每个网页相当于一个使用者，因此就在页面加载时生成一次 ID（并写入 localStorage 以保证重新打开页面时沿用之前的 id）
const OWNER_ID_STORAGE_KEY = 'service-container-owner-id'
const defaultOwnerId =
  localStorage.getItem(OWNER_ID_STORAGE_KEY) ?? Math.ceil(Math.random() * 1000000000).toString()
localStorage.setItem(OWNER_ID_STORAGE_KEY, defaultOwnerId)

export class Container extends Service<Services> {
  get communicate() {
    return this.services.mqtt.communicate
  }
  get auth() {
    return this.services.auth
  }

  registered() {
    void this.auth.accountId.then(() => {
      this.initAutoRefresh()
      this.listenError()
    })
  }

  /**
   * 当前可用来登录的容器
   */
  usable = new MemSubject<Usable>({ status: Status.Empty })

  /**
   * 获取可用来登录的容器
   */
  async getUsable() {
    this.usable.next({ status: Status.Loading })
    const res = await this.request()
    if (res.success) {
      logger.debug(`发现容器：${res.data.hostname} ${res.data.qrcode}`)
      this.usable.next({ status: Status.Normal, data: res.data })
    } else {
      logger.warn(`获取容器失败：${res.error}`)
      this.usable.next({ status: Status.Error, message: res.error })
    }
  }

  /**
   * 自动刷新当前可用容器
   */
  private initAutoRefresh() {
    setInterval(() => {
      const usable = this.usable.value
      const now = new Date().getTime()
      if (
        usable.status === Status.Normal &&
        usable.data.created.getTime() + CONTAINER_PERIOD <= now
      ) {
        logger.debug('自动刷新容器')
        void this.getUsable()
      }
    }, 1000 * 10)
  }

  /**
   * 忘记当前可用的容器（例如它已被使用，或不再需要使用）
   */
  forget() {
    this.usable.next({ status: Status.Empty })
  }

  /**
   * 发起请求，获取一个新的容器
   * （通常不应直接使用，而应通过 usable / getUsable 取得容器信息）
   *
   * 可根据需要指定一个不同的 ownerId 以保证取得没被当前网页使用过的容器
   */
  async request(ownerId?: string) {
    return API('/Container/CreateContainer', {
      data: { random: ownerId ?? defaultOwnerId },
      formatter(data: RespContainerData): ContainerData {
        return {
          hostname: data.hostname,
          qrcode: data.qrcode,
          created: new Date(data.createtime),
        }
      },
    })
  }

  /**
   * 销毁容器（允许传入非可用列表中的 hostname）
   */
  destroy(hostnames: string | string[]) {
    if (typeof hostnames === 'string') hostnames = [hostnames]
    logger.info('销毁容器', hostnames)
    void API('/Container/RemoveContainer', { data: { hostname: hostnames } })
  }

  /**
   * 监听容器内部异常信息
   */
  private listenError() {
    this.communicate.manage(50000).subscribe(message => {
      logger.warn('container error', message)
    })
  }
}
